package cn.hallele.leinsteining.ms.support.starter.components.audit.aspect;

import cn.hallele.leinsteining.mc.infrastructure.metadata.constants.SymbolConstant;
import cn.hallele.leinsteining.mc.infrastructure.util.date.DateUtils;
import cn.hallele.leinsteining.ms.infrastructure.util.network.HttpUtil;
import cn.hallele.leinsteining.ms.support.starter.components.audit.annotation.ApiLog;
import cn.hallele.leinsteining.ms.support.starter.components.audit.io.ApiLogIO;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.core.annotation.Order;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;

import java.util.Enumeration;
import java.util.StringJoiner;

/**
 * ApiLogAspect
 *
 * @author anle5
 * @since 2024/8/11 21:43
 */
@Slf4j
@Aspect
@Order(20)
@Component
public class ApiLogAspect {
    @Value("${spring.application.name:Unknown}")
    private String applicationName;

    @Value("${leinsteining.support.audit-stream-name:audit-out-0}")
    private String auditStreamName;

    @Autowired
    private StreamBridge streamBridge;

    @Pointcut("@annotation(cn.hallele.leinsteining.ms.support.starter.components.audit.annotation.ApiLog)")
    private void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        ApiLogIO.ApiLogIOBuilder logIO = ApiLogIO.builder();
        try {
            logIO.applicationName(applicationName)
                    .ip(HttpUtil.getClientIp())
                    .device(HttpUtil.getClientInfo())
//                .initiator()
                    .tranceId(HttpUtil.getTraceId());

            // 拼接报文
            HttpServletRequest request = HttpUtil.getHttpServletRequest();
            StringJoiner requestBody = new StringJoiner(System.lineSeparator());
            requestBody.add(request.getMethod() + SymbolConstant.SPACE + request.getRequestURI())
                    .add(SymbolConstant.EMPTY)
                    .add(getHeader(request));
            logIO.request(requestBody.toString());

            // 业务信息
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            logIO.apiId(methodSignature.getMethod().getAnnotation(ApiLog.class).id());

            // 时间、响应
            logIO.startTime(DateUtils.now());
            Object response = joinPoint.proceed();
            logIO.response(response);

            return response;
        } catch (Exception e) {
            logIO.response(e.getMessage());
            throw e;
        } finally {
            logIO.endTime(DateUtils.now());
            streamBridge.send(auditStreamName, MessageBuilder.withPayload(logIO.build()).build());
        }
    }

    private String getHeader(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        StringJoiner joiner = new StringJoiner(System.lineSeparator());
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String header = request.getHeader(headerName);
            joiner.add(headerName + SymbolConstant.COLON + header);
        }
        return joiner.toString();
    }
}
